﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using BusTransfer;
namespace ModbusCommunicate
{
    public  class SendByTcp 
    {
        Socket socket;
        /// <summary>
        /// 初始化数据结构
        /// </summary>
        public struct InitData
        {
            /// <summary>
            /// ip地址
            /// </summary>
            public string ip;
            /// <summary>
            /// IP端口    
            /// </summary>
            public int port;
            /// <summary>
            /// 注册数据帧
            /// </summary>
            public byte[] register;
            /// <summary>
            /// 心跳包
            /// </summary>
            public byte[] heartbeat;
            /// <summary>
            /// 心跳时间，单位秒
            /// 等于0不发心跳包
            /// </summary>
            public int heartbeatfreq;

            /// <summary>
            /// 注册信息保存
            /// </summary>
            public ModbusReg MR;

        }

        public struct  ModbusEvent
        {
            /// <summary>
            /// Modbus注册信息
            /// </summary>
            public ModbusReg MR;

            /// <summary>
            /// 事件描述
            /// </summary>
            public string Detail;

            /// <summary>
            /// 事件类型
            /// </summary>
            public MBEvent events;
        }

       public  enum MBEvent
        {
            /// <summary>
            /// tcp连接成功
            /// </summary>
           TcpConnected,
           /// <summary>
           /// tcp连接失败
           /// </summary>
           TcpConnectFail,
           /// <summary>
           /// TCP发送数据失败
           /// </summary>
           TcpSendfail,
        }

        private InitData _tcpinfo = new InitData();
        internal InitData TCPinfo
        {
            set { _tcpinfo = value; }        
            get{  return _tcpinfo; }
        }

        internal void  TCPConfig(InitData Data)
        {
            TCPinfo = Data;
          
        }

       public  System.Threading.AutoResetEvent Conn = new System.Threading.AutoResetEvent(false );

        /// <summary>
        /// TCP是否正确连接
        /// </summary>
        public bool  TcpStarted { get; set; }


        /// <summary>
        /// 同步连接
        /// </summary>
        internal    bool   TCPStart()
        {
           return  TCPCONNECT();
        }


        private   bool   TCPCONNECT()
        {        
            Conn.Reset();
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse(TCPinfo.ip);
            IPEndPoint iep = new IPEndPoint(ip, TCPinfo.port);
            socket.BeginConnect(iep, new AsyncCallback(Connect), socket);
            Conn.WaitOne();
            if (!socket.Connected)
            {
                TcpStarted = false;
                return false;
            }
            TcpStarted = true;
            return true;
        }


        internal void AsyncTcpStart()
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse(TCPinfo.ip);
            IPEndPoint iep = new IPEndPoint(ip, TCPinfo.port);
            socket.BeginConnect(iep, new AsyncCallback(Connect), socket);
        }




        /// <summary>
        /// TCP服务开启成功
        /// </summary>
        /// <param name="ar"></param>
        private void Connect(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;
                handler.EndConnect(ar);
                ReceiveData();
                Register();
                if (TCPinfo.heartbeatfreq != 0)
                {
                    HeartBeat = new System.Timers.Timer(TCPinfo.heartbeatfreq * 1000);
                    HeartBeat.Elapsed += HeartBeat_Elapsed;
                    HeartBeat.Start();
                }
                MsgTransfer.Instance.Send<ModbusEvent>(new ModbusCommunicate.SendByTcp.ModbusEvent()
                {
                    MR = TCPinfo.MR,
                    Detail = "连接成功",
                    events = MBEvent.TcpConnected
                }, TCPinfo.MR.reg_id + "@modbus");
            }
            catch (Exception ex)
            {
                MsgTransfer.Instance.Send<ModbusEvent>(new ModbusCommunicate.SendByTcp.ModbusEvent()
                {
                    MR = TCPinfo.MR,
                    Detail = ex.Message ,
                    events = MBEvent.TcpConnectFail  
                }, TCPinfo.MR.reg_id + "@modbus");

            }
            finally
            {
                Conn.Set();
            }
   

        }
        /// <summary>
        /// 用于定时发送心跳包
        /// </summary>
        System.Timers.Timer HeartBeat;

        private void HeartBeat_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Send(TCPinfo.heartbeat);
        }

      /// <summary>
      /// 有数据交互时重置计时器
      /// </summary>
        private void HeartRestart()
        {
            if (HeartBeat != null)
            {
                HeartBeat.Stop();  //重置计时时间
                HeartBeat.Start(); //重新开始
            }
        }

        /// <summary>
        /// 发送注册包
        /// </summary>
        private void Register()
        {
            Send(TCPinfo.register);
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data"></param>
        internal  bool Send(byte[] data)
        {
            try
            {
                socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), socket);
            }
            catch (Exception ex)
            {
                BusTransfer.MsgTransfer.Instance.Send<ModbusEvent>(new ModbusEvent() {  Detail=ex.Message,
                 events= MBEvent.TcpSendfail  , MR=TCPinfo.MR  },TCPinfo.MR.reg_id+"@modbus");
                AsyncTcpStart();
                return false;

            }
            return true;
        }

        /// <summary>
        /// 数据发送成功
        /// </summary>
        /// <param name="ar"></param>
        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;
                handler.EndSend(ar);
                HeartRestart();
            }
            catch (Exception)
            {
                ;
            }

        }

        /// <summary>
        /// 接收数据缓存
        /// </summary>
        byte[] MsgBuffer = new byte[10240];
        /// <summary>
        /// 开启异步接收数据
        /// </summary>
        private  void ReceiveData()
        {
            socket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null);
        }



        private void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                int REnd = socket.EndReceive(ar);
                if (REnd > 0)
                {
                    HeartRestart();
                    //接收到的数据
                    byte[] data = new byte[REnd];
                    Array.Copy(MsgBuffer, 0, data, 0, REnd);
                    socket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null);
                    if (RS != null)
                        RS(data);
                 
                }
                else
                {
                    dispose();
                }
            }
            catch (Exception ex )
            {
                string s = ex.Message.ToString();
            }
        }



        internal void dispose()
        {
            try
            {
                this.socket.Shutdown(SocketShutdown.Both);
                this.socket.Close();
                if(HeartBeat!=null )
                HeartBeat.Elapsed -= HeartBeat_Elapsed;
            }
            catch (Exception)
            { }
        }

        /// <summary>
        /// 收到的数据通过委托传给主方法
        /// </summary>
        /// <param name="R"></param>
        public void delegateinit(ReceiveSocketData R)
        {
            RS = R;
        }

        ReceiveSocketData RS;
       public  delegate void ReceiveSocketData(byte[] Data);


    }
}
